//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: CacheManagerBase.cs
//
//  Description: Offers basic methods for caching manager.
// 
//--------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Chili.Opf3.Mapping;
using Chili.Opf3.Query;
using Chili.Opf3.Storages;

namespace Chili.Opf3.Caching
{
	/// <summary>
	/// Class offers some basic methods for caching manager.
	/// </summary>
	/// <remarks>
	/// This abstract class offers some basic methods for caching manager classes. The methods allow
	/// fast building of cache keys for a persistent object or to equal two keys.
	/// </remarks>
	public abstract class CacheManagerBase : ICacheManager
	{
		#region ICacheManager Members

		/// <summary>
		/// Adds a persistent object to the cache.
		/// </summary>
		/// <param name="obj">The object that is added.</param>
		public abstract bool AddPersistentObject(object obj);

		/// <summary>
		/// Gets an instance of the <see cref="ObjectReader{T}">ObjectReader</see> for the given query.
		/// </summary>
		/// <param name="context">The <see cref="ObjectContext">ObjectContext</see> used to load the reader.</param>
		/// <param name="query">A class that implements the <see cref="IQuery">IQuery</see> interface.</param>
		public abstract ObjectReader<T> GetObjectReader<T>(ObjectContext context, IQuery query);

		#endregion

		/// <summary>
		/// Builds a key for the object. This key may be used in the cache. The key that is 
		/// created contains also information about the type of the persistent object.
		/// </summary>
		/// <param name="obj">The persistent object that's key is build.</param>
		protected string BuildUniqueKey(object obj)
		{
			ChecksumBuilder builder = new ChecksumBuilder();
			TypeMapping mapping = TypeMapping.GetTypeMapping(obj);

			// Adds the type of the object to the key.
			builder.AddMember(obj.GetType().FullName.ToString(), null);

			// Loop over all identifier mappings and create the key.
			foreach (IMemberInfo m in mapping.Members.GetIdentifierMemberMappings())
			{
				// Add the value of the member to the checksum.
				builder.AddMember(m.GetValue(obj), m.FieldAttribute);
			}

			return ChecksumToString(builder.ToChecksum());
		}

		/// <summary>
		/// Builds a key for the given type an data record. This key may be used in the cache.
		/// The key that is created contains also information about the type of the persistent object.
		/// </summary>
		/// <param name="type">The type of the persistent object.</param>
		/// <param name="dataRecord">The data record used to create the key.</param>
		protected string BuildUniqueKey(Type type, IDataRecord dataRecord)
		{
			ChecksumBuilder builder = new ChecksumBuilder();
			TypeMapping mapping = TypeMapping.GetTypeMapping(type);

			// Adds the type of the object to the key.
			builder.AddMember(type.FullName.ToString(), null);

			// Loop over all identifier mappings and create the key.
			foreach (IMemberInfo m in mapping.Members.GetIdentifierMemberMappings())
			{
				// Add the value of the member to the checksum.
				builder.AddMember(dataRecord[m.FieldAttribute.FieldName], m.FieldAttribute);
			}

			return ChecksumToString(builder.ToChecksum());
		}

		/// <summary>
		/// Builds a key for the given query. This key may be used in the cache.
		/// </summary>
		/// <param name="storage">The storage on which the query is executed.</param>
		/// <param name="query">The query whose key is generated.</param>
		protected string BuildUniqueKey(IStorage storage, IQuery query)
		{
			ChecksumBuilder builder = new ChecksumBuilder();

			// Compile the query.
			CompiledQuery cq = query.ToCompiledQuery(storage);
			IDbCommand command = cq.Command;

			// Add the type of the storage to the key.
			builder.AddMember(storage.GetType().AssemblyQualifiedName, null);
			// Add the text of the command to the key.
			builder.AddMember(command.CommandText, null);

			// Adds the parameters to the key.
			foreach (IDataParameter parameter in command.Parameters)
			{
				builder.AddMember(parameter.Value, null);
			}

			return this.ChecksumToString(builder.ToChecksum());
		}

		/// <summary>
		/// Converts the byte checksum to a string.
		/// </summary>
		/// <param name="checksum">The checksum to convert.</param>
		private string ChecksumToString(byte[] checksum)
		{
			StringBuilder builder = new StringBuilder();
			for (int i = 0; i < checksum.Length; i++)
			{
				builder.Append(checksum[i]);
				builder.Append(":");
			}
			return builder.ToString();
		}
	}
}
